﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ServiceModel.Web;
using Microsoft.Http;
using System.IO;
using System.Xml.Serialization;
using System.ServiceModel;
using CoderJoe.WoW.Services;
using System.Drawing;

namespace CoderJoe.WoW.Proxies.Armory
{
	public class WoWArmoryProxy
		: IWoWArmory
	{
		public const string BaseUrlPath = "http://www.wowarmory.com/";

		protected TResult ExecuteMethod<TResult>(MethodBase method, params object[] arguments)
		{
			return (TResult)ExecuteMethod(method, typeof(TResult), arguments);
		}

		protected void ExecuteMethod(MethodBase method, params object[] arguments)
		{
			ExecuteMethod(method, typeof(void), arguments);
		}

		protected object ExecuteMethod(MethodBase method, Type returnType, params object[] arguments)
		{
			WebGetAttribute attr = FindWebGetAttribute(method);
			Dictionary<string, string> parameters = ParseParameters(method, arguments);
			Uri uri = BuildUri(attr, parameters);

			using (HttpClient client = new HttpClient(uri))
			{
				client.DefaultHeaders.Add("User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0;)");
				using (Stream response = client.Get().EnsureStatusIsSuccessful().Content.ReadAsStream())
				{
					if (returnType == typeof(void))
					{
						return null;
					}
					else if (returnType == typeof(Stream))
					{
						MemoryStream result = new MemoryStream();
						const int BufferLength = 2048;

						byte[] buffer = new byte[BufferLength];
						int amountRead = 0;
						do
						{
							amountRead = response.Read(buffer, 0, BufferLength);
							result.Write(buffer, 0, amountRead);
						} while (amountRead > 0);

						return result;
					}

					XmlSerializer serializer = new XmlSerializer(returnType);
					return serializer.Deserialize(response);
				}
			}
		}

		private static WebGetAttribute FindWebGetAttribute(MethodBase method)
		{
			WebGetAttribute attr = (WebGetAttribute)Attribute.GetCustomAttribute(method, typeof(WebGetAttribute));
			if (attr == null)
			{
				Type[] possibleContracts = method.DeclaringType.GetInterfaces();
				foreach(Type t in possibleContracts)
				{
					if(Attribute.IsDefined(t, typeof(ServiceContractAttribute)))
					{
						InterfaceMapping mapping = method.DeclaringType.GetInterfaceMap(t);
						int methodIndex = Array.IndexOf(mapping.TargetMethods, method);
						if(methodIndex >= 0)
						{
							attr = (WebGetAttribute)Attribute.GetCustomAttribute(mapping.InterfaceMethods[methodIndex], typeof(WebGetAttribute));
							if (attr != null)
								break;
						}
					}
				}
			}
			return attr;
		}

		private Dictionary<string, string> ParseParameters(MethodBase method, object[] arguments)
		{
			ParameterInfo[] parameters = method.GetParameters();
			Dictionary<string, string> result = new Dictionary<string, string>();

			for (int i = 0; i < parameters.Length; i++)
			{
				result.Add(parameters[i].Name, arguments[i].ToString());
			}

			return result;
		}

		private Uri BuildUri(WebGetAttribute attr, Dictionary<string, string> arguments)
		{
			UriTemplate template = new UriTemplate(attr.UriTemplate);
			Uri baseUri = new Uri(BaseUrlPath);

			return template.BindByName(baseUri, arguments);
		}

		public Character GetCharacter(string realm, string name)
		{
			Character character = new Character();
			GetCharacterSheetResult.page characterSheet = GetCharacterSheet(realm, name);

			character.BattleGroup = characterSheet.characterInfo.character.battleGroup;
			character.CharacterClass = characterSheet.characterInfo.character.@class;
			character.Name = characterSheet.characterInfo.character.name;
			character.Realm = characterSheet.characterInfo.character.realm;

			foreach (var remoteItem in characterSheet.characterInfo.characterTab.items)
			{
                if (remoteItem.slot >= 0)
                {
                    CharacterItem item = new CharacterItem();

                    item.CurrentDurability = remoteItem.durability;
                    item.Durability = remoteItem.maxDurability;
                    item.EnchantId = remoteItem.permanentenchant;
                    item.Gems[0] = remoteItem.gem0Id;
                    item.Gems[1] = remoteItem.gem1Id;
                    item.Gems[2] = remoteItem.gem2Id;
                    item.Icon = remoteItem.icon;
                    item.ItemId = remoteItem.id;
                    item.SlotIndex = remoteItem.slot;

                    character.Items[item.SlotIndex] = item;
                }
			}

			return character;
		}

		public Item GetItem(int itemId)
		{
			Item item = new Item();
			var remoteItem = GetItemInfo(itemId);

			item.Icon = remoteItem.itemInfo.item.icon;
			item.ItemId = remoteItem.itemInfo.item.id;
			item.Level = remoteItem.itemInfo.item.level;
			item.Name = remoteItem.itemInfo.item.icon;
			item.Quality = remoteItem.itemInfo.item.quality;
			item.Type = remoteItem.itemInfo.item.type;

			return item;
		}

        public ItemDetail GetItemDetail(int itemId)
        {
            ItemDetail item = new ItemDetail();
            var remoteItem = GetItemDetailInfo(itemId);

            item.ItemId = remoteItem.itemTooltips.itemTooltip.id;
            item.Icon = remoteItem.itemTooltips.itemTooltip.icon;

            if (remoteItem.itemTooltips.itemTooltip.armor != null)
            {
                item.Armor = remoteItem.itemTooltips.itemTooltip.armor.Value;
                item.BonusArmor = remoteItem.itemTooltips.itemTooltip.armor.armorBonus;
            }

            item.BonusIntellect = remoteItem.itemTooltips.itemTooltip.bonusIntellect;
            item.BonusHitRating = remoteItem.itemTooltips.itemTooltip.bonusHitRating;
            item.InventoryTypeId = remoteItem.itemTooltips.itemTooltip.equipData.inventoryType;
            item.SubClassName = remoteItem.itemTooltips.itemTooltip.equipData.subclassName; ;
            item.Bonding = remoteItem.itemTooltips.itemTooltip.bonding;
            item.Name = remoteItem.itemTooltips.itemTooltip.name;
            item.QualityId = remoteItem.itemTooltips.itemTooltip.overallQualityId;
            item.ClassId = remoteItem.itemTooltips.itemTooltip.classId;
            item.BonusStamina = remoteItem.itemTooltips.itemTooltip.bonusStamina;
            item.BonusSpellPower = remoteItem.itemTooltips.itemTooltip.bonusSpellPower;
            item.BonusResilienceRating = remoteItem.itemTooltips.itemTooltip.bonusResilienceRating;

            item.AllowedClasses = remoteItem.itemTooltips.itemTooltip.allowableClasses;

            return item;
        }

		public Image GetItemImage(string imageName)
		{
			using (Stream buffer = GetItemImageStream(imageName))
			{
				return Image.FromStream(buffer);
			}
		}

		#region IWoWArmory Members

		public GetCharacterSheetResult.page GetCharacterSheet(string realm, string name)
		{
			return ExecuteMethod<GetCharacterSheetResult.page>(
				System.Reflection.MethodInfo.GetCurrentMethod(),
				realm, name);
		}

		public GetItemInfoResult.page GetItemInfo(int itemId)
		{
			return ExecuteMethod<GetItemInfoResult.page>(
				System.Reflection.MethodInfo.GetCurrentMethod(),
				itemId);
		}

		public Stream GetItemImageStream(string imageName)
		{
			return ExecuteMethod<Stream>(
				System.Reflection.MethodInfo.GetCurrentMethod(),
				imageName);
		}

        public GetItemDetailResult.page GetItemDetailInfo(int itemId)
        {
            return ExecuteMethod<GetItemDetailResult.page>(
                System.Reflection.MethodInfo.GetCurrentMethod(),
                itemId);
        }

        #endregion
    }
}
